﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nature.Text
{    
    public class TextSerializer
    {        
        public event TextInfoEventHandler TextInfoMessage;
        public event TextInfoEventHandler TextInfoError;
        public event TextInfoEventHandler TextInfoWarning;

        public T Deserialize<T>(string text) where T : TextFragment
        {
            Type textFragmentType = typeof(T);
            var textSourceType = textFragmentType.GetCustomAttributes(typeof(TextSourceAttribute), true).OfType<TextSourceAttribute>().Select(att => att.TextSourceType)
                .SingleOrDefault()
                ?? typeof(TextSource);
            var constructor = textSourceType.GetConstructor(new Type[] { typeof(string) });
            if (constructor == null) { throw new InvalidOperationException(); }
            var textSource = (TextSource)constructor.Invoke(new object[] { text });

            var textInfoMessageListener = new TextInfoMessageListener();
            var textInfoMessageHandler = this.TextInfoMessage;
            if (textInfoMessageHandler != null)
            {
                textInfoMessageListener.TextInfoMessage += textInfoMessageHandler;
            }
            try
            {
                constructor = textFragmentType.GetConstructor(new Type[] { textSourceType, typeof(ITextInfoEventListener) });
                if (constructor == null) { throw new InvalidOperationException(); }
                object obj = constructor.Invoke(new object[] { textSource, textInfoMessageListener });
                if (textInfoMessageListener.Errors.Count() > 0)
                {
                    throw new TextFormatException(textInfoMessageListener.Errors);
                }
                return (T)obj;
            }
            finally
            {
                if (textInfoMessageHandler != null)
                {
                    textInfoMessageListener.TextInfoMessage -= textInfoMessageHandler;
                }
            }
        }

        private void RouteMessage(object sender, TextInfoEventArgs args)
        {
            TextInfoEventHandler handler = null;
            switch (args.InfoType)
            {
                case(TextInfoType.Error):
                    if ((handler = this.TextInfoError) != null)
                    {
                        handler(sender, args);
                    }
                    break;
                case (TextInfoType.Warning):
                    if ((handler = this.TextInfoWarning) != null)
                    {
                        handler(sender, args);
                    }
                    break;
            }
        }

        class TextInfoMessageListener : ITextInfoEventListener
        {
            private readonly List<TextInfoEventArgs> m_textInfoEventArgs = new List<TextInfoEventArgs>();

            public event TextInfoEventHandler TextInfoMessage;

            public IEnumerable<TextInfoEventArgs> Errors
            {
                get { return m_textInfoEventArgs.Where(i => i.InfoType == TextInfoType.Error).OrderBy(i => i.Fragment); }
            }

            public IEnumerable<TextInfoEventArgs> Warnings
            {
                get { return m_textInfoEventArgs.Where(i => i.InfoType == TextInfoType.Warning).OrderBy(i => i.Fragment); }
            }

            #region ITextInfoEventListener Members

            public void Submit(TextInfoEventArgs args)
            {
                m_textInfoEventArgs.Add(args);
                TextInfoEventHandler handler = TextInfoMessage;                
                if (handler != null)
                {
                    handler(this, args);
                }
            }

            #endregion
        }
    }
}
